package com.haiyi.activiti.engine.framework.activiti.manager.impl;

import com.baomidou.mybatisplus.toolkit.IdWorker;
import com.google.common.collect.Maps;
import com.haiyi.activiti.engine.model.entity.BpmnDefintion;
import com.haiyi.activiti.engine.model.entity.FormModel;
import com.haiyi.activiti.engine.model.entity.Process;
import com.haiyi.activiti.engine.model.entity.User;
import com.haiyi.activiti.engine.exception.ActivitiEngineException;
import com.haiyi.activiti.engine.exception.DaoException;
import com.haiyi.activiti.engine.framework.activiti.manager.FormManager;
import com.haiyi.activiti.engine.framework.activiti.manager.ProcessManager;
import com.haiyi.activiti.engine.framework.basic.DateTimeAdapter;
import com.haiyi.activiti.engine.framework.basic.IdBuilder;
import com.haiyi.activiti.engine.framework.basic.SecurityAdapter;
import com.haiyi.activiti.engine.service.IBpmnDefintionService;
import com.haiyi.activiti.engine.service.IProcessService;
import com.haiyi.activiti.engine.util.Assert;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

/**
 * <p> </p>
 *
 * <pre> Created: 2018/12/11 15:53 </pre>
 *
 * @author hlu
 * @version 1.0
 * @since JDK 1.7
 */
@Service
public class ProcessManagerImpl implements ProcessManager {

    private static Logger log = LoggerFactory.getLogger(ProcessManagerImpl.class);

    @Autowired
    private IProcessService processService;
    @Autowired
    private IdBuilder idBuilder;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private SecurityAdapter securityAdapter;
    @Autowired
    private DateTimeAdapter dateTimeAdapter;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private FormManager formManager;
    @Autowired
    private IBpmnDefintionService bpmnDefintionService;

    @Override
    public List<Process> selectAll() {
        return processService.selectList(null);
    }

    @Override
    public void saveProcess(Process process) {
        Assert.isNotNull(process,"process不能为空！");
        if(process.getIid()==null){
            //插入
            process.setIid(idBuilder.getId());
            setCreateProcessTimeAndUser(process);
            processService.insert(process);
        }else{
            //更新
            setUpdateProcessTimeAndUser(process);
            processService.updateById(process);
        }
    }

    @Override
    public void removeProcess(String processId) {
        final Process process = processService.selectById(processId);
        final String flowId = process.getFlowId();
        final Deployment deployment = repositoryService.createDeploymentQuery().processDefinitionKey(flowId).singleResult();
        repositoryService.deleteDeployment(deployment.getId(),true);

        Assert.isTrue(processService.deleteById(processId),"删除流程失败");
    }

    @Override
    public ProcessInstance startProcess(String processId, Map<String,Object> variables) {
        final Process process = processService.selectById(processId);
        Assert.isNotNull(process,"根据processId="+processId+"查询的process不存在！");

        //先流程引擎验证下启动参数是否合法
        if(process.getStartFormId()==null){
            log.warn("流程id:{}没有配置开始表单！",processId);
        }else {
            final FormModel formModel = formManager.createFormModelQuery().iid(process.getStartFormId()).singleResult();

            if (formModel == null) {
                log.warn("流程id:{}配置了开始表单id:｛｝，但开始表单不存在！", processId,process.getStartFormId());
            } else {
                formManager.submitStartForm(formModel, variables);
            }
        }

        String businessKey = IdWorker.getId()+"";

        final String flowId = process.getFlowId();
        final ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey(flowId)
                .latestVersion() // 获取最后一个版本
                .singleResult();
        Assert.isNotNull(processDefinition,"业务流程尚未发布，请与管理员联系！");

        Map<String,Object> params = Maps.newHashMap();
        params.put(VAR_START_FORM_DATA,variables);
        params.put(VAR_USER_ID,securityAdapter.getCurrentUser().getIid());

        final ProcessInstance processInstance =
                runtimeService.startProcessInstanceById(processDefinition.getId(),businessKey,params);

        return processInstance;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deploy(String processId, String bpmXmlStr) {

        String processKey = PROCESSID_PREFIX + processId;
        bpmXmlStr = StringUtils.replace(bpmXmlStr,"${processKey}",processKey);

        BpmnDefintion bpmnDefintion = new BpmnDefintion();
        bpmnDefintion.setCreater(securityAdapter.getCurrentUser().getIid());
        bpmnDefintion.setCreateTime(dateTimeAdapter.getCurrentDate());
        bpmnDefintion.setDesignXml(bpmXmlStr);
        bpmnDefintion.setIid(IdWorker.getId()+"");

        Assert.isTrue(bpmnDefintionService.insert(bpmnDefintion),"流程部署失败！");
        deploy(bpmnDefintion,processId);
    }

    @Override
    public void deploy(String processId) {
        final Process process = processService.selectById(processId);
        final String bpmnDefintionId = process.getBpmnId();
        final BpmnDefintion bpmnDefintion = bpmnDefintionService.selectById(bpmnDefintionId);
        deploy(bpmnDefintion,processId);
    }


    private void deploy(BpmnDefintion bpmnDefintion,String processId){

        final Process process = processService.selectById(processId);
        Assert.isNotNull(process,"根据processId="+processId+"查询的process不存在！");

        process.setBpmnId(bpmnDefintion.getIid());
        Assert.isTrue(processService.updateById(process),"流程部署失败！");


        String processKey = PROCESSID_PREFIX + processId;

        String bpmXmlStr = bpmnDefintion.getDesignXml();

        final Deployment deploy = repositoryService.createDeployment()
                .addString(processKey + ".bpmn", bpmXmlStr)
                .name(processKey).deploy();

        final long count = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .latestVersion()
                .count();



        if(count>0){
            final ProcessDefinition processDefinition =
                    this.repositoryService.createProcessDefinitionQuery()
                            .processDefinitionKey(processKey)
                            .latestVersion()
                            .singleResult();

            process.setVersion(processDefinition.getVersion());
            process.setStatus(PROCESS_STATUS_NORMAL);
            process.setFlowId(processKey);

            setUpdateProcessTimeAndUser(process);
            Assert.isTrue(processService.updateById(process),"流程部署失败！");
        }else{
            throw new ActivitiEngineException("流程部署失败！");
        }
    }


    private void setUpdateProcessTimeAndUser(Process process){
        final User currentUser = securityAdapter.getCurrentUser();
        process.setUpdateTime(dateTimeAdapter.getCurrentDate());
        process.setUpdater(currentUser.getIid());
    }

    private void setCreateProcessTimeAndUser(Process process){
        final User currentUser = securityAdapter.getCurrentUser();
        process.setCreateTime(dateTimeAdapter.getCurrentDate());
        process.setCreater(currentUser.getIid());
    }
}
